package tmtimer;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Map;
import java.util.Properties;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import com.darwinsys.swingui.UtilGUI;

/**
 * TMTimer - simple speech timer for Toastmasters or similar events where
 * speeches are time-limited. 
 * TODO: fiddle gui builder to obviate fixGUI().
 * 
 * @author Ian F. Darwin, https://darwinsys.com/
 */
public class TMTimer extends JFrame implements Runnable {

	private static final long serialVersionUID = 8733835671732822383L;

	/** Constructor - Initializes the Form */
	public TMTimer() {
		initComponents();
		finishGUI();
		gotoState(READY);
		pack();
		UtilGUI.centre(this);
	}

	/**
	 * This method is called from within the constructor to initialize the form.
	 * WARNING: Do NOT modify this code. The content of this method is always
	 * regenerated by the FormEditor.
	 */
	private void initComponents() {// GEN-BEGIN:initComponents
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent evt) {
				exitForm();
			}
		});
		getContentPane().setLayout(new BorderLayout());

		jMenuBar1 = new JMenuBar();
		fileMenu = new JMenu();
		fileMenu.setText("File");
		exitMenuItem = new JMenuItem();
		exitMenuItem.setText("Exit");
		exitMenuItem.addActionListener(evt->jMenuItem1ActionPerformed(evt));

		fileMenu.add(exitMenuItem);

		jMenuBar1.add(fileMenu);

		setJMenuBar(jMenuBar1);
		buttonsPanel = new JPanel();
		buttonsPanel.setLayout(new FlowLayout());

		startButton = new JButton();
		startButton.setText("Start");
		startButton.addActionListener(evt->jButton1ActionPerformed(evt));
		buttonsPanel.add(startButton);

		stopButton = new JButton();
		stopButton.setText("Stop");
		stopButton.addActionListener(evt -> stopButtonActionPerformed(evt));
		buttonsPanel.add(stopButton);

		resetButton = new JButton();
		resetButton.setText("Reset");
		resetButton.addActionListener(evt -> resetButtonActionPerformed(evt));
		buttonsPanel.add(resetButton);

		getContentPane().add(buttonsPanel, "South");

		timesPanel = new JPanel();
		timesPanel.setLayout(new BorderLayout());

		presetsPanel = new JPanel();
		presetsPanel.setLayout(new FlowLayout());

		presetsLabel = new JLabel();
		presetsLabel.setText("Presets:");
		presetsPanel.add(presetsLabel);

		presetsList = new JList<String>();
		presetsList.setMinimumSize(new Dimension(100, 100));
		presetsPanel.add(presetsList);

		timesPanel.add(presetsPanel, "West");

		fieldsPanel = new JPanel();
		fieldsPanel.setLayout(new GridLayout(3, 2));

		greenLabel = new JLabel();
		greenLabel.setBackground(Color.green);
		greenLabel.setText("Green");
		greenLabel.setHorizontalAlignment(SwingConstants.RIGHT);
		fieldsPanel.add(greenLabel);

		greenTF = new JTextField();
		greenTF.setText("0:00");
		fieldsPanel.add(greenTF);

		yellowLabel = new JLabel();
		yellowLabel.setBackground(Color.yellow);
		yellowLabel.setText("Yellow");
		fieldsPanel.add(yellowLabel);

		yellowTF = new JTextField();
		yellowTF.setText("0:00");
		fieldsPanel.add(yellowTF);

		redLabel = new JLabel();
		redLabel.setBackground(Color.red);
		redLabel.setText("Red");
		fieldsPanel.add(redLabel);

		redTF = new JTextField();
		redTF.setText("0:00");
		fieldsPanel.add(redTF);

		timesPanel.add(fieldsPanel, BorderLayout.CENTER);

		runningTimesPanel = new JPanel();
		runningTimesPanel.setLayout(new BoxLayout(runningTimesPanel, 1));

		curTime = new JLabel();
		curTime.setHorizontalTextPosition(SwingConstants.CENTER);
		curTime.setText("0:00");
		curTime.setHorizontalAlignment(SwingConstants.CENTER);
		curTime.setFont(new Font("Dialog", 3, 36));
		runningTimesPanel.add(curTime);

		maxTime = new JLabel();
		maxTime.setHorizontalTextPosition(SwingConstants.CENTER);
		maxTime.setText("0:00");
		maxTime.setHorizontalAlignment(SwingConstants.CENTER);
		maxTime.setFont(new Font("Dialog", 3, 36));
		runningTimesPanel.add(maxTime);

		timesPanel.add(runningTimesPanel, BorderLayout.EAST);

		getContentPane().add(timesPanel, BorderLayout.NORTH);

		bigFlag = new JLabel();
		bigFlag.setPreferredSize(new Dimension(500, 300));
		bigFlag.setOpaque(true);
		bigFlag.setHorizontalAlignment(SwingConstants.CENTER);
		getContentPane().add(bigFlag, BorderLayout.CENTER);

	}// GEN-END:initComponents

	/** The action handler for the Start button (doStart) */
	private void jButton1ActionPerformed(ActionEvent evt) {// GEN-FIRST:event_jButton1ActionPerformed
		// Add your handling code here:
		if (TMTimerUtil.mmssToInt(greenTF.getText()) <= 0
				|| TMTimerUtil.mmssToInt(yellowTF.getText()) <= 0
				|| TMTimerUtil.mmssToInt(redTF.getText()) <= 0) {
			JOptionPane.showMessageDialog(this,
					"All time values must be non-zero", "Error",
					JOptionPane.ERROR_MESSAGE);
			return;
		}
		maxTime.setText(redTF.getText());
		gotoState(RUNNING);
		new Thread(this).start();
	}// GEN-LAST:event_jButton1ActionPerformed

	/** The action handler for the Stop button (doStop) */
	private void stopButtonActionPerformed(ActionEvent evt) {// GEN-FIRST:event_stopButtonActionPerformed
		// Add your handling code here:
		gotoState(STOPPED);
	}// GEN-LAST:event_stopButtonActionPerformed

	/** The action handler for the Reset button (doReset) */
	private void resetButtonActionPerformed(ActionEvent evt) {// GEN-FIRST:event_resetButtonActionPerformed
		// Add your handling code here:
		gotoState(READY);
	}// GEN-LAST:event_resetButtonActionPerformed

	/** Action handler for the File->Exit menu item. */
	private void jMenuItem1ActionPerformed(ActionEvent evt) {
		System.exit(0);
	}
	// GEN-LAST:event_jMenuItem1ActionPerformed

	/** Exit the Application */
	private void exitForm() {
		System.exit(0);
	}
	// GEN-LAST:event_exitForm

	// Variables declaration - do not modify//GEN-BEGIN:variables
	private JPanel buttonsPanel;
	private JPanel timesPanel;
	private JLabel bigFlag;
	private JButton startButton;
	private JButton stopButton;
	private JButton resetButton;
	private JPanel presetsPanel;
	private JPanel fieldsPanel;
	private JPanel runningTimesPanel;
	private JLabel presetsLabel;
	private JList<String> presetsList;
	private JLabel greenLabel;
	private JTextField greenTF;
	private JLabel yellowLabel;
	private JTextField yellowTF;
	private JLabel redLabel;
	private JTextField redTF;
	private JLabel curTime;
	private JLabel maxTime;
	private JMenuBar jMenuBar1;
	private JMenu fileMenu;
	private JMenuItem exitMenuItem;
	// End of variables declaration//GEN-END:variables

	protected final int READY = 0;
	protected final int RUNNING = 1;
	protected final int GREEN = 2;
	protected final int YELLOW = 3;
	protected final int RED = 4;
	protected final int STOPPED = 5;

	// These values are arbitrarily chosen not to intersect with the above.
	protected final int C_NONE = 100;
	protected final int C_GREEN = 101;
	protected final int C_YELLOW = 102;
	protected final int C_RED = 103;

	/** A halt-the-timer flag */
	protected boolean done;
	/** Iterator that maps from a presets name to its list of seconds. */
	protected Map<String, int[]> presetsMap;
	/** Map to make array processing of times easier */
	protected JTextField[] times = new JTextField[3];
	/** The three numbers for the current event */
	int[] data = new int[3];

	/**
	 * Attend to a few details that the GUI doesn't seem to drive. Called at end
	 * of Constructor.
	 */
	protected void finishGUI() {
		// double widths[] = { .33, .66 };
		// fieldsPanel.setLayout(new EntryLayout(widths));
		yellowLabel.setHorizontalAlignment(SwingConstants.RIGHT);
		redLabel.setHorizontalAlignment(SwingConstants.RIGHT);
		times[0] = greenTF;
		times[1] = yellowTF;
		times[2] = redTF;
		presetsList.addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				gotoState(READY);
				String key = (String) presetsList.getSelectedValue();
				data = presetsMap.get(key);
				for (int i = 0; i < data.length; i++)
					times[i].setText(TMTimerUtil.intToMmss(data[i]));
			}
		});
	}

	public void showTime(String newTime) {
		curTime.setText(newTime);
	}

	/* The start button creates a Thread to run this. */
	public void run() {
		// start where we left off! doReset sets it to zero
		int time = TMTimerUtil.mmssToInt(curTime.getText());
		if (time == 0)
			gotoState(RUNNING);
		done = false;
		data[0] = TMTimerUtil.mmssToInt(greenTF.getText());
		data[1] = TMTimerUtil.mmssToInt(yellowTF.getText());
		data[2] = TMTimerUtil.mmssToInt(redTF.getText());
		while (!done) {
			try {
				showTime(TMTimerUtil.intToMmss(time));
				/*
				 * Adjust color display. Tests in reverse order to avoid
				 * flashing.
				 */
				if (time == data[2])
					gotoState(RED);
				else if (time == data[1])
					gotoState(YELLOW);
				else if (time == data[0])
					gotoState(GREEN);
				Thread.sleep(1000);
				time++;
			} catch (InterruptedException e) {
				// nothing
			}
		}
	}

	/** Set the color on the screen and/or LED's. */
	protected void setColor(int c) {
		switch (c) {
		case C_NONE:
			bigFlag.setBackground(Color.white);
			break;
		case C_GREEN:
			bigFlag.setBackground(Color.green);
			break;
		case C_YELLOW:
			bigFlag.setBackground(Color.yellow);
			break;
		case C_RED:
			bigFlag.setBackground(Color.red);
			break;
		default:
			throw new IllegalStateException("setColor(" + c + ") invalid");
		}
	}

	/** Set the program into the given state. */
	protected void gotoState(int st) {
		switch (st) {
		case READY:
			curTime.setText(TMTimerUtil.intToMmss(0));
			maxTime.setText(redTF.getText());
			setColor(C_NONE);
			/* FALLTHROUGH INTENTIONAL */
		case STOPPED:
			done = true;
			stopButton.setEnabled(false);
			startButton.setEnabled(true);
			break;
		case RUNNING:
			startButton.setEnabled(false);
			stopButton.setEnabled(true);
			break;
		case GREEN:
			setColor(C_GREEN);
			break;
		case YELLOW:
			setColor(C_YELLOW);
			break;
		case RED:
			setColor(C_RED);
			break;
		default:
			throw new IllegalStateException("gotoState(" + st + ") invalid");
		}
	}

	/** Fill in the fields in the Presets list. */
	protected void populateList(Properties p) {
		String[] preAlloc = new String[p.size()];
		presetsList.setListData((String[]) p.keySet().toArray(preAlloc));
	}

	/** Main program to create the whole thing. */
	public static void main(java.lang.String[] args) {
		TMTimer t = new TMTimer();
		Properties p = new Properties();
		TMTimerUtil.loadProperties(p, "tmtimer/TMTimer");
		t.presetsMap = TMTimerUtil.parseProps(p);
		t.populateList(p);
		t.setVisible(true);
	}
}
